import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

enum Tabulation {
    Strike, Spare, OpenFrame, None
}

class BowlingGame {
    private final List<Integer> pinList = new ArrayList<>(26);

    void roll(int pins) {
        if (pins == 10) {
            this.pinList.add(pins);
            this.pinList.add(0);
        } else {
            this.pinList.add(pins);
        }
    }

    boolean isEndOfTheGame() {
        if (this.pinList.size() == 20) if (this.pinList.get(18) != 10)
            if (this.pinList.get(19) != 10) if ((this.pinList.get(18) + this.pinList.get(19)) < 10) return true;

        if (this.pinList.size() == 21) if (this.pinList.get(18) != 10) if (this.pinList.get(19) != 10)
            if ((this.pinList.get(18) + this.pinList.get(19)) == 10) if (this.pinList.get(20) != 10) return true;

        if (this.pinList.size() == 22)
            if ((this.pinList.get(18) + this.pinList.get(19)) == 10) if (this.pinList.get(20) != 10) return true;

        if (this.pinList.size() == 22) if (this.pinList.get(18) != 10)
            if (this.pinList.get(19) != 10) if ((this.pinList.get(18) + this.pinList.get(19)) == 10) return true;

        if (this.pinList.size() == 23) if (this.pinList.get(18) == 10) if (this.pinList.get(20) == 10) return true;

        if (this.pinList.size() == 24) if (this.pinList.get(18) == 10) if (this.pinList.get(20) == 10) return true;

        return false;
    }

    int score() {
        if (this.pinList.size() < 20)
            throw new IllegalStateException("Score cannot be taken until the end of the game");

        if (this.pinList.size() > 20 && this.pinList.get(18) + this.pinList.get(19) < 10)
            throw new IllegalStateException("Cannot roll after game is over");

        if (!this.isEndOfTheGame()) {
            throw new IllegalStateException("Score cannot be taken until the end of the game");
        }

        // fill '0' to pinlist and make pinlist length to 26.
        IntStream.range(0, (26 - this.pinList.size())).forEach((number) -> this.pinList.add(0));

        int result = 0;
        for (int i = 0; i < 10; i++) {
            result += this.getCurrentFrameScore(i, 1);
        }
        return result;
    }

    private int getCurrentFrameScore(int frame, int deep) {
        int firstThrowScore = this.pinList.get(frame * 2);
        int secondThrowScore = this.pinList.get(frame * 2 + 1);

        if (firstThrowScore < 0 || secondThrowScore < 0)
            throw new IllegalStateException("Negative roll is invalid");

        if (firstThrowScore > 10 || secondThrowScore > 10)
            throw new IllegalStateException("Pin count exceeds pins on the lane");

        if (firstThrowScore + secondThrowScore > 10)
            throw new IllegalStateException("Pin count exceeds pins on the lane");

        Tabulation tabulation = this.getTabulationFrame(firstThrowScore, secondThrowScore);

        int result = 0;
        switch (tabulation) {
            case Strike:
                if (deep == 3)
                    result = 10;
                else
                    result = firstThrowScore + secondThrowScore + this.getCurrentFrameScore(frame + 1, deep + 1);
                break;
            case Spare:
                if (deep == 3)
                    result = firstThrowScore;
                else if (this.pinList.get((frame + 1) * 2) == 10)
                    result = firstThrowScore + secondThrowScore + this.getCurrentFrameScore(frame + 1, deep + 1);
                else
                    result = firstThrowScore + secondThrowScore + this.pinList.get((frame + 1) * 2);

                break;
            case OpenFrame:
                result = firstThrowScore + secondThrowScore;
                break;
            case None:
            default:
                break;
        }

        return result;
    }

    private Tabulation getTabulationFrame(int fir, int sec) {
        if (this.isAStrike(fir, sec)) return Tabulation.Strike;
        if (this.isASpare(fir, sec)) return Tabulation.Spare;
        if (this.isAnOpenFrame(fir, sec)) return Tabulation.OpenFrame;

        return Tabulation.None;
    }

    private boolean isAStrike(int fir, int sec) {
        return (fir == 10) && (sec == 0);
    }

    private boolean isASpare(int fir, int sec) {
        return (fir > 0) && (sec > 0) && (fir + sec == 10);
    }

    private boolean isAnOpenFrame(int fir, int sec) {
        return (fir >= 0) && (sec >= 0) && (fir + sec < 10);
    }
}